Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Data masking] Ensure @unmask can be used with the fragment registry #12029

Merged

Conversation

jerelmiller
Copy link
Member

Fixes #12028

Ensures that fragments registered with the fragment registry can be looked up by the data masking algorithm.

This change adds a new lookupFragment API in the base cache class that allows custom caches to provide lookups for fragments by name. For example, InMemoryCache has the fragment registry, so it overrides the base implementation and uses the fragment registry to lookup a fragment by name.

As a part of this, I've moved the implementation of maskFragment and maskOperation over to QueryManager where I believe it fits better. This makes it a much more private API and makes it much more obvious that custom cache implementations don't need to interact with these APIs directly since we want the core Apollo Client controlling the implementation.

@jerelmiller jerelmiller added 🐞 bug 🎭 data-masking Issues/PRs related to data masking labels Aug 24, 2024
@jerelmiller jerelmiller requested a review from phryneas August 24, 2024 04:14
Copy link

changeset-bot bot commented Aug 24, 2024

⚠️ No Changeset found

Latest commit: f6466d8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@@ -170,11 +148,18 @@ export abstract class ApolloCache<TSerialized> implements DataProxy {
// If not implemented by a cache subclass, data masking will effectively be
// disabled since we will not be able to accurately determine if a given type
// condition for a union or interface matches a particular type.
protected fragmentMatches?(
public fragmentMatches?(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not super fond of this name and perhaps this PR is a good time to change it (this was introduced with data masking by the way). This function is essentially responsible for the possibleTypes-like API which map interface/union types to concrete types. This ensures we can traverse the right selection set when given a type in a response that is part of an interface.

InMemoryCache has the possibleTypes API, but not all caches are guaranteed to have this. Any ideas on a more descriptive name here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some possibilities:

  • fragmentMatchesTypename
  • fragmentMatchesPolymorphicType
  • fragmentConditionIncludesTypename

I'm not a huge fan of these either, but I guess what I don't like about the original name is that it feels like it needs something after the "matches" part to understand what its matching against. I just can't seem to put my finger on the right word to use here.

Copy link
Member

@phryneas phryneas Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about splitting up part of the policies.fragmentMatches(fragment, typename, result, variables) api into a policies.satisfiesSupertype(typename, supertype)?
result and variables are both optional, and if they are not given, the only fragment property that is acessed is fragment.typeCondition anyways.
This would result in a satisfiesSupertype(typename, supertype) api here without any relationship to fragment, with a call like context.cache.satisfiesSupertype!(data.__typename, selection.typeCondition) in masking.ts.

Alternative name: satisfiesTypeCondition or implementsSupertype.

That would also make is a lot less intimidating for other caches to implement this method - fragmentMatches implies a lot more functionality (and our implementation does a lot more unrelated stuff).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like these suggestions way more than anything I had and dig the idea of having a separate policies function for this. For that, I'll split that out to a separate PR so that I can experiment with it and don't bury it in this one.

// Function used to lookup a fragment when a fragment definition is not part
// of the GraphQL document. This is useful for caches, such as InMemoryCache,
// that register fragments ahead of time so they can be referenced by name.
public lookupFragment(fragmentName: string): FragmentDefinitionNode | null {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we like this more terse, or do we prefer something a bit more verbose like lookupFragmentByName?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine either way.

@jerelmiller jerelmiller requested a review from alessbell August 26, 2024 19:22
Base automatically changed from jerel/mask-fragments to data-masking August 27, 2024 15:56
@jerelmiller jerelmiller force-pushed the jerel/fix-fragment-matching-fragment-registry branch from ee1488e to 2b6beb2 Compare August 27, 2024 15:58
): TData {
if (!cache.fragmentMatches) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of these changes were just moved from original cache implementation

Copy link
Member

@phryneas phryneas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, some minor suggestions :)

// Function used to lookup a fragment when a fragment definition is not part
// of the GraphQL document. This is useful for caches, such as InMemoryCache,
// that register fragments ahead of time so they can be referenced by name.
public lookupFragment(fragmentName: string): FragmentDefinitionNode | null {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine either way.

src/core/masking.ts Outdated Show resolved Hide resolved
@github-actions github-actions bot added the auto-cleanup 🤖 label Aug 28, 2024
@jerelmiller jerelmiller force-pushed the jerel/fix-fragment-matching-fragment-registry branch from 96d1a49 to 5dbab89 Compare August 28, 2024 14:32
@jerelmiller jerelmiller merged commit a5ef5a8 into data-masking Aug 28, 2024
32 checks passed
@jerelmiller jerelmiller deleted the jerel/fix-fragment-matching-fragment-registry branch August 28, 2024 15:00
jerelmiller added a commit that referenced this pull request Aug 28, 2024
jerelmiller added a commit that referenced this pull request Aug 28, 2024
jerelmiller added a commit that referenced this pull request Sep 24, 2024
jerelmiller added a commit that referenced this pull request Sep 24, 2024
jerelmiller added a commit that referenced this pull request Sep 24, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto-cleanup 🤖 🐞 bug 🎭 data-masking Issues/PRs related to data masking
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants